#ifndef EANIMATION_H
#define EANIMATION_H

#include "../Engine_global.h"
#include "esprite.h"

template <typename T, typename Controller>
class ENGINESHARED_EXPORT EAnimation
{
    typedef T (ESprite::*fGetter)() const;
    fGetter m_getter;

    typedef void (ESprite::*fSetter)(T value);
    fSetter m_setter;

    typedef bool (Controller::*fUpdater)(T& value, float dt);
    fUpdater m_updater;

    typedef void (Controller::*fOnAnimationFinished)();
    fOnAnimationFinished m_onAnimationFinished;

    Controller* m_pController;
    boost::weak_ptr<ESprite> m_sprite;
    bool m_bLoop;
    bool m_bFinished;
    T m_startValue;
    bool m_bStopped;
public:
    EAnimation(boost::shared_ptr<ESprite> sprite, Controller* pController)
        : m_sprite(sprite)
        , m_pController(pController)
        , m_getter(0)
        , m_setter(0)
        , m_updater(0)
        , m_onAnimationFinished(0)
        , m_bLoop(false)
        , m_bFinished(false)
        , m_startValue(T())
        , m_bStopped(false)
    { }

    void setGetter(fGetter getter)
    {
        m_getter = getter;
    }

    void setSetter(fSetter setter)
    {
        m_setter = setter;
    }

    void setUpdater(fUpdater updater)
    {
        m_updater = updater;
    }

    void update(float dt)
    {
        if(m_bStopped || m_bFinished)
        {
            return;
        }

        T value = (m_sprite.lock().get()->*m_getter)();
        bool bFinished = (m_pController->*m_updater)(value, dt);
        (m_sprite.lock().get()->*m_setter)(value);

        if(!bFinished)
        {
            // do nothing
        }
        else if(m_bLoop)
        {
            value = m_startValue;
        }
        else if(m_onAnimationFinished)
        {
            m_bFinished = true;
            (m_pController->*m_onAnimationFinished)();
        }
    }

    void activate()
    {

    }

    void deactivate()
    {

    }

    void setLoop(bool bLoop)
    {
        m_bLoop = bLoop;
    }

    bool isLoop() const
    {
        return m_bLoop;
    }

    bool isFinished() const
    {
        return m_bFinished;
    }

    void setStartValue(const T& value)
    {
        m_startValue = value;
    }

    void setValue(const T& value)
    {
        (m_sprite.lock().get()->*m_setter)(value);
    }

    void reset()
    {
        (m_sprite.lock().get()->*m_setter)(m_startValue);
    }

    void stop()
    {
        m_bStopped = true;
    }

    void play()
    {
        m_bStopped = false;
    }

    bool isStopped() const
    {
        return m_bStopped;
    }
};

template <typename T, typename UpdaterClass>
class ENGINESHARED_EXPORT EAnimationSet
{
    typedef std::map<std::string, boost::shared_ptr<EAnimation<T, UpdaterClass> > > Animations;
    Animations m_animations;

    boost::weak_ptr<EAnimation<T, UpdaterClass> > m_currentAnimation;
public:
    bool addAnimation(const std::string& name, boost::shared_ptr<EAnimation<T, UpdaterClass> > anim)
    {
        if(m_animations.find(name) != m_animations.end()) return false;
        m_animations[name] = anim;
        return true;
    }

    bool setCurrentAnimation(const std::string& name)
    {
        if(m_animations.find(name) == m_animations.end()) return false;
        if(m_currentAnimation.lock())
        {
            m_currentAnimation.lock()->deactivate();
        }

        m_currentAnimation = m_animations[name];

        m_currentAnimation.lock()->activate();
    }

    void update(float dt)
    {
        if(m_currentAnimation.lock())
        {
            m_currentAnimation.lock()->update(dt);
        }
    }
};

#endif // EANIMATION_H
